package com.ysh.xiaomi.web.servelt;

import com.ysh.xiaomi.utils.StringConstans;
import com.ysh.xiaomi.utils.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;

/**
 * 袁胜虎 on 2022/8/16 19:40
 */
@WebServlet(urlPatterns="/")
    public class BaseServlet extends HttpServlet {
    private HashMap<String,Object> url2ServletMapping = new HashMap<>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
           /*
     当前servlet是一个通用的servlet,它的作用是将我们的请求进行接,然后转给对应的类中的对应的方法执行
     我们怎么知道一个请求地址对应什么类和什么方法?我们提前规划好了一个规则,然后我们就按照这个规则执行
     //比如我们约定这个请求地址就是servlet的名字的前缀,然后请求的参数中有一个method参数,代表要执行的方法

        //我们现在就需要做以下事情

        获取到请求的地址,这个代表的是类名


        //获取到method参数,这个代表方法的名字


        根据上面的类名反射得到class对象


        根据方法名反射得到method对象

        通过class反射创建对象,然后调用上面的方法,把参数传过去,得到返回结果


        //我们还不知道返回结果怎么处理, 所以我们又需要定义规则,返回的东西要么是转发,要么是重定向, 要么就是数据直接写出去

        我们约定 forward:开头的代表转发, redirect:开头的代表重定向,其他的代表原样写出去
     */
        //请求的地址,/开头,不包括queryparama
        String uri = req.getRequestURI();
        //我们约定过uri是servlet名字的前缀
        uri = uri.replace("/", "");
        //获取到首字母
        char c = uri.charAt(0);
        //转换成大写
        uri = uri.replace(c, (char) (c - 32));
        //拼接类名
        String servletName = this.getClass().getPackage().getName() + "." + uri + "Servlet";
        //反射得到class对象
        try {
            Class<?> targetServletClass = Class.forName(servletName);
            //获取到请求的方法名
            String method = req.getParameter("method");
            //如果没有传递method,我们约定就用一个默认方法来执行 比如叫defaultMethod
            if (StringUtils.isEmpty(method)) {
                //代表没有传递方法,根据我们的约定,来处理,比如返回404或者使用默认的方法名
                method = "defaultMethod";
            }
            //获取到Method对象,从我们想要执行的servlet中获取,我们传递的参数就是request和response
            Method targetServletClassMethod = targetServletClass.getMethod(method, HttpServletRequest.class, HttpServletResponse.class);
            //执行方法,我们不需要每次都创建一个新的对象,所以只需要创建一次
//            Object servlet = url2ServletMapping.get(servletName);
//            if (servlet==null){
//                servlet=targetServletClass.newInstance();
//                url2ServletMapping.put(servletName,servlet);
//            }
            //从map中获取指定key 的数据,如果不存在,就通过后面的方法创建并保存进去
            Object servlet = url2ServletMapping.computeIfAbsent(servletName, key -> {
                try {
                    return targetServletClass.newInstance();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
            //执行方法并获取结果
            Object result = targetServletClassMethod.invoke(servlet, req, resp);
            if (result instanceof String) {
                if (((String) result).startsWith(StringConstans.FORWARD)) {
                    //转发
                    req.getRequestDispatcher(((String) result).replace(StringConstans.FORWARD, "")).forward(req,resp);
                } else if (((String) result).startsWith(StringConstans.REDIRECT)) {
                    //重定向
                    resp.sendRedirect(((String) result).replace(StringConstans.REDIRECT, ""));
                }else{
                    //原始内容直接返回
                    resp.getWriter().write((String) result);
                }
            }
        } catch (Exception e) {
            //代表类不存在,其实就代表着这个请求地址不存在
            //throw new RuntimeException(e)
            e.printStackTrace();
            resp.sendError(404);
        }
        System.err.println("servletName:=====>" + servletName);
    }
}
